/*
 * Wazuh Send report test
 * Copyright (C) 2015, Wazuh Inc.
 * November 22, 2023.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#include "eventSendReport_test.hpp"
#include "../../../../shared_modules/utils/flatbuffers/include/rsync_generated.h"
#include "../../../../shared_modules/utils/flatbuffers/include/rsync_schema.h"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_deltas_generated.h"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_deltas_schema.h"
#include "../scanOrchestrator/eventSendReport.hpp"
#include "MockOsDataCache.hpp"
#include "MockReportDispatcher.hpp"
#include "TrampolineOsDataCache.hpp"
#include "TrampolineRemediationDataCache.hpp"
#include "flatbuffers/idl.h"

using ::testing::_;

const size_t MAX_RETRIES {10};
auto constexpr TEST_REPORTS_QUEUE_PATH {"queue/vd/reports"};
auto constexpr TEST_REPORTS_BULK_SIZE {1};
auto constexpr TEST_REPORTS_THREADS_NUMBER {1};

namespace NSEventSendReportTest
{

    const std::string SYNC_STATE_MSG {
        R"(
            {
                "agent_info": {
                    "agent_id": "001",
                    "agent_ip": "192.168.33.20",
                    "agent_name": "focal"
                },
                "data_type": "state",
                "data": {
                    "attributes_type": "syscollector_packages",
                    "attributes": {
                        "architecture": " ",
                        "checksum": "d24e16553ac8f6983f16fb7a68b841ac8876d745",
                        "description": " ",
                        "format": "pypi",
                        "groups": " ",
                        "install_time": " ",
                        "item_id": "e051a99ad3950084b538c6538c0fcad0f1c4c713",
                        "location": "/usr/lib/python3/dist-packages/language_selector-0.1.egg-info/PKG-INFO",
                        "name": "language-selector",
                        "priority": " ",
                        "scan_time": "2023/12/01 20:48:21",
                        "size": 0,
                        "source": " ",
                        "vendor": " ",
                         "version": "0.1"
                    },
                    "index": "e051a99ad3950084b538c6538c0fcad0f1c4c713",
                }
            }
        )"};

    const std::string SYNC_STATE_ALERT {
        R"(
        {
            "vulnerability": {
                "category": "Packages",
                "classification": "CVSS",
                "cve": "CVE-2020-14343",
                "description": "A vulnerability was discovered in the PyYAML library in versions before 5.4, where it is susceptible to arbitrary code execution when it processes untrusted YAML files through the full_load method or with the FullLoader loader. Applications that use the library to process untrusted input may be vulnerable to this flaw. This flaw allows an attacker to execute arbitrary code on the system by abusing the python/object/new constructor. This flaw is due to an incomplete fix for CVE-2020-1747.",
                "enumeration": "CVE",
                "package": {
                    "architecture": " ",
                    "build_version": "",
                    "checksum": "",
                    "description": " ",
                    "install_scope": "",
                    "install_time": " ",
                    "license": "",
                    "name": "PyYAML",
                    "path": "/usr/lib/python3/dist-packages/PyYAML-5.3.1.egg-info",
                    "reference": "",
                    "size": 0,
                    "type": "pypi",
                    "version": "5.3.1"
                },
                "reference": "https://bugzilla.redhat.com/show_bug.cgi?id=1860466, https://github.com/SeldonIO/seldon-core/issues/2252, https://github.com/yaml/pyyaml/issues/420, https://www.oracle.com/security-alerts/cpuapr2022.html, https://www.oracle.com/security-alerts/cpujul2022.html",
                "score": {
                    "base": 9.800000190734863,
                    "environmental": 0.0,
                    "temporal": 0.0,
                    "version": "3.1"
                },
                "severity": "Critical",
                "status": "Active"
            }
        }
    )"};

    const std::string DELTA_DELETE_MSG {
        R"(
            {
                "agent_info": {
                    "agent_id": "001",
                    "agent_ip": "192.168.33.20",
                    "agent_name": "focal"
                },
                "data_type": "dbsync_packages",
                "data": {
                    "architecture": "amd64",
                    "checksum": "dfc12dd72d4b32c6b2c6b598c1999f8578c2ee89",
                    "description": "Text editor",
                    "format": "deb",
                    "groups": "editors",
                    "item_id": "772cafce68754b17864ade66be2d66730b5706e4",
                    "multiarch": "foreign",
                    "name": "vim",
                    "priority": "important",
                    "scan_time": "2023/08/04 20:03:45",
                    "size": 205,
                    "source": "vim",
                    "vendor": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
                    "version": "2:8.1.2269-1ubuntu5.21"
                },
                "operation": "DELETED"
            }
    )"};

    const std::string DELTA_DELETE_ALERT {
        R"(
            {
                "vulnerability": {
                    "category": "Packages",
                    "classification": "CVSS",
                    "cve": "CVE-2023-5441",
                    "description": "NULL Pointer Dereference in GitHub repository vim/vim prior to 20d161ace307e28690229b68584f2d84556f8960.",
                    "enumeration": "CVE",
                    "package": {
                        "architecture": "amd64",
                        "name": "vim",
                        "version": "2:8.1.2269-1ubuntu5.21"
                    },
                    "reference": "https://huntr.dev/bounties/b54cbdf5-3e85-458d-bb38-9ea2c0b669f2, https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/VDDWD25AZIHBAA44HQT75OWLQ5UMDKU3/, https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/VGTVLUV7UCXXCZAIQIUCLG6JXAVYT3HE/, https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/XPT7NMYJRLBPIALGSE24UWTY6F774GZW/, https://github.com/vim/vim/commit/20d161ace307e28690229b68584f2d84556f8960",
                    "score": {
                    "base": "6.200000",
                    "version": "3.0"
                    },
                    "severity": "Medium",
                    "status": "Solved"
                }
            }
        )"};

    const std::string DELTA_INSERT_OS {
        R"({
          "agent_info": {
            "agent_id": "001",
            "agent_ip": "192.168.33.20",
            "agent_name": "focal"
          },
          "data_type": "dbsync_osinfo",
          "data": {
              "architecture":"x86_64",
              "checksum":"1691178971959743855",
              "hostname":"fd9b83c25f30",
              "os_major":"15",
              "os_name":"SLES",
              "os_platform":"sles",
              "os_version":"15-SP5",
              "release":"5.4.0-155-generic",
              "scan_time":"2023/08/04 19:56:11",
              "sysname":"Linux",
              "version":"#172-Ubuntu SMP Fri Jul 7 16:10:02 UTC 2023"
          },
          "operation": "INSERTED"
       })"};

    const std::string DELTA_UPDATE_HOTFIX {
        R"({
          "agent_info": {
            "agent_id": "001",
            "agent_ip": "192.168.33.20",
            "agent_name": "focal"
          },
          "data_type":"dbsync_hotfixes",
          "data":
          {
             "hotfix":"KB123456",
             "checksum":"abcdef0123456789"
          },
          "operation":"MODIFIED"
        })"};

    // Helpers

    const Os osData {.hostName = "osdata_hostname",
                     .architecture = "osdata_architecture",
                     .name = "osdata_name",
                     .codeName = "osdata_codeName",
                     .majorVersion = "osdata_majorVersion",
                     .minorVersion = "osdata_minorVersion",
                     .patch = "osdata_patch",
                     .build = "osdata_build",
                     .platform = "osdata_platform",
                     .version = "osdata_version",
                     .release = "osdata_release",
                     .displayVersion = "osdata_displayVersion",
                     .sysName = "osdata_sysName",
                     .kernelVersion = "osdata_kernelVersion",
                     .kernelRelease = "osdata_kernelRelease"};

    void expectOsData()
    {
        spOsDataCacheMock = std::make_shared<MockOsDataCache>();
        EXPECT_CALL(*spOsDataCacheMock, getOsData(_, _))
            .WillOnce(testing::Invoke(
                [](const std::string&, Os& osDataResult)
                {
                    osDataResult = osData;
                    return true;
                }));
    }

} // namespace NSEventSendReportTest

using namespace NSEventSendReportTest;

TEST_F(EventSendReportTest, SendFormattedMsg)
{
    // Mock report dispatcher.
    auto reportDispatcher = std::make_shared<MockReportDispatcher>();
    // Send report instance.
    TEventSendReport<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                     MockReportDispatcher>
        sendReport(reportDispatcher);

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(rsync_SCHEMA));
    ASSERT_TRUE(parser.Parse(SYNC_STATE_MSG.c_str()));
    const uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorSync = Synchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorSync);
    nlohmann::json detectionJson = nlohmann::json::parse(SYNC_STATE_ALERT);
    scanContext->m_alerts["CVE-2020-14343"] = detectionJson;

    EXPECT_CALL(*reportDispatcher, push(_)).Times(1);

    // Send report.
    sendReport.handleRequest(scanContext);
}

TEST_F(EventSendReportTest, InvalidEncodingValue)
{
    // Mock report dispatcher.
    auto reportDispatcher = std::make_shared<MockReportDispatcher>();
    // Send report instance.
    TEventSendReport<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                     MockReportDispatcher>
        sendReport(reportDispatcher);

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_deltas_SCHEMA));
    ASSERT_TRUE(parser.Parse(DELTA_DELETE_MSG.c_str()));
    const uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorSync = SyscollectorDeltas::GetDelta(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorSync);
    nlohmann::json detectionJson = nlohmann::json::parse(SYNC_STATE_ALERT);
    detectionJson["data"]["package"]["name"] = "\xAA";
    scanContext->m_alerts["CVE-2020-14343"] = detectionJson;

    EXPECT_CALL(*reportDispatcher, push(_)).Times(0);

    // Send report.
    sendReport.handleRequest(scanContext);
}

TEST_F(EventSendReportTest, SendFormattedDeltaMsg)
{
    // Mock report dispatcher.
    auto reportDispatcher = std::make_shared<MockReportDispatcher>();
    // Send report instance.
    TEventSendReport<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                     MockReportDispatcher>
        sendReport(reportDispatcher);

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_deltas_SCHEMA));
    ASSERT_TRUE(parser.Parse(DELTA_DELETE_MSG.c_str()));
    const uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = SyscollectorDeltas::GetDelta(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    nlohmann::json detectionJson = nlohmann::json::parse(DELTA_DELETE_ALERT);
    scanContext->m_alerts["CVE-2023-5441"] = detectionJson;

    EXPECT_CALL(*reportDispatcher, push(_)).Times(1);

    // Send report.
    EXPECT_NO_THROW(sendReport.handleRequest(scanContext));
}

TEST_F(EventSendReportTest, SendFormattedDeltaMsgOS)
{
    // Mock report dispatcher.
    auto reportDispatcher = std::make_shared<MockReportDispatcher>();
    // Send report instance.
    TEventSendReport<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                     MockReportDispatcher>
        sendReport(reportDispatcher);

    Os osData {.hostName = "osdata_hostname",
               .architecture = "osdata_architecture",
               .name = "osdata_name",
               .codeName = "osdata_codeName",
               .majorVersion = "osdata_majorVersion",
               .minorVersion = "osdata_minorVersion",
               .patch = "osdata_patch",
               .build = "osdata_build",
               .platform = "osdata_platform",
               .version = "osdata_version",
               .release = "osdata_release",
               .displayVersion = "osdata_displayVersion",
               .sysName = "osdata_sysName",
               .kernelVersion = "osdata_kernelVersion",
               .kernelRelease = "osdata_kernelRelease"};

    spOsDataCacheMock = std::make_shared<MockOsDataCache>();
    EXPECT_CALL(*spOsDataCacheMock, setOsData(_, _)).Times(1);

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_deltas_SCHEMA));
    ASSERT_TRUE(parser.Parse(DELTA_INSERT_OS.c_str()));
    const uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = SyscollectorDeltas::GetDelta(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    scanContext->m_alerts["CVE-2023-5441"] = nlohmann::json::object();

    EXPECT_CALL(*reportDispatcher, push(_)).Times(1);

    // Send report.
    EXPECT_NO_THROW(sendReport.handleRequest(scanContext));
}

TEST_F(EventSendReportTest, SendFormattedDeltaMsgHotfix)
{
    // Mock report dispatcher.
    auto reportDispatcher = std::make_shared<MockReportDispatcher>();
    // Send report instance.
    TEventSendReport<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                     MockReportDispatcher>
        sendReport(reportDispatcher);

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_deltas_SCHEMA));
    ASSERT_TRUE(parser.Parse(DELTA_UPDATE_HOTFIX.c_str()));
    const uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = SyscollectorDeltas::GetDelta(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    scanContext->m_alerts["CVE-2023-5441"] = nlohmann::json::object();

    EXPECT_CALL(*reportDispatcher, push(_)).Times(1);

    // Send report.
    EXPECT_NO_THROW(sendReport.handleRequest(scanContext));
}
